use std::{fs::{self, read}, io::{BufReader, Read}};

use axum::{
    routing::{get, get_service, post},
    Router, response::Html, http::StatusCode, Form,
};
use serde::Deserialize;
use tower_http::services::{self, ServeDir};

use aes::{Aes128, cipher::{generic_array::GenericArray, ArrayLength}};

use aes_gcm::{
    aead::{KeyInit, OsRng},
    AesGcm,
    Aes256Gcm, Nonce, AeadInPlace // Or `Aes128Gcm`
};

#[derive(Deserialize)]
struct AesGcmEncryptInfo {
    input:  String,
    key:    String,
    nonce:  String,
    aad:    String,
}


async fn html() -> Html<String> {

    let file = fs::File::open("./static/index.html").unwrap();
    let mut reader = BufReader::new(file);

    let mut buf = String::new();
    if let Ok(_) = reader.read_to_string(&mut buf) {
        return Html(buf);
    } else {
        return Html("not found".to_string());
    }
}

type Aes128GCM = AesGcm<Aes128, typenum::U16>;

async fn encryptbyaes128gcm(Form(frm): Form<AesGcmEncryptInfo>) -> String {
    println!(" plain: {:?}\n key: {:?}\n nonce: {:?}\n aad: {:?}\n", frm.input, frm.key, frm.nonce, frm.aad);

    let key = hex::decode(frm.key).unwrap();
    let key = GenericArray::from_slice(&key);
    println!("key {}  {:?}", key.len(), key);
    let cipher = Aes128GCM::new(&key);

    let nonce = hex::decode(frm.nonce).unwrap();
    let nonce = GenericArray::from_slice(&nonce);
    let aad = frm.aad.as_bytes();

    let mut buffer = Vec::new();
    buffer.extend_from_slice(&frm.input.as_bytes());

    let tag = cipher.encrypt_in_place_detached(nonce, &aad, &mut buffer).unwrap();

    println!("tag: {} {:#02x}", tag.len(), tag);
    println!("cipherText: {} {:#02x?}",buffer.len(), buffer);

    let mut res = Vec::with_capacity(nonce.len() + tag.len() + buffer.len());
    res.append(&mut nonce.as_slice().to_vec());
    res.append(&mut tag.as_slice().to_vec());
    res.append(&mut buffer);

    return hex::encode(res);
}

async fn decryptbyaes128gcm(Form(frm): Form<AesGcmEncryptInfo>) -> String {
    println!("xxxx");
    println!(" plain: {:?}\n key: {:?}\n nonce: {:?}\n aad: {:?}\n", frm.input, frm.key, frm.nonce, frm.aad);

    let key = hex::decode(frm.key).unwrap();
    let key = GenericArray::from_slice(&key);
    println!("key {}  {:?}", key.len(), key);
    let cipher = Aes128GCM::new(&key);

    let input = hex::decode(frm.input.as_bytes()).unwrap();

    let nonce = input[0..16].to_vec();
    let nonce = GenericArray::from_slice(&nonce);

    let tag = input[16..32].to_vec();
    let tag = GenericArray::from_slice(&tag);

    let aad = frm.aad.as_bytes();

    let mut buffer = Vec::new();
    buffer.extend_from_slice(&input[32..]);

    println!("tag: {:#02x?}", tag);
    println!("buffer: {:#02x?}", buffer);

    cipher.decrypt_in_place_detached(nonce, &aad, &mut buffer, &tag).unwrap();

    return String::from_utf8(buffer).unwrap();
}

async fn test() -> &'static str {
    println!("t test.......");
    return "test";
}

#[tokio::main]
async fn main() {

    let serve_dir_service = get_service(ServeDir::new("static"));

    // build our application with a single route
    let app = Router::new()
        .nest_service(
            "/static",
            serve_dir_service,
        )
        .route("/", get(html))
        .route("/test", get(test))
        .route("/encryptbyaes128gcm", post(encryptbyaes128gcm))
        .route("/decryptbyaes128gcm", post(decryptbyaes128gcm));

    // run it with hyper on localhost:3000
    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}